﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using mmp = MessageSystem.Managed.Serialization;

namespace RpcGenerator.CSharp
{
    class ShareFieldProtobufGenerator
    {
        public void Generate(MessageFieldDescription description, IMessageSerializer serializer)
        {
            BeginExtension(description, serializer);

            GenerateRead(description, serializer);
            GenerateWrite(description, serializer);

            EndExtension(serializer);
        }

        private void BeginExtension(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine(string.Format("public static class {0}Extension", description.Type.Name));
            serializer.BeginBlock();
        }

        private void EndExtension(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private void GenerateWrite(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine(string.Format("public static void Write(this {0} {1},NetStream ns)", description.Type.Name, "_this"));
            serializer.BeginBlock();
            {
                int nFieldIndex = 0;
                foreach (MessageFieldMemberDescription mfmd in description.Members)
                {
                    Type type = mfmd.Type;
                    switch (mfmd.GetMessageFieldType())
                    {
                        case MessageFieldType.PrimitiveType:
                            {
                                WritePrimitiveType(mfmd.Type, string.Format("{0}.{1}", "_this", mfmd.Name), nFieldIndex++, serializer,true);
                                break;
                            }
                        case MessageFieldType.GenericType:
                            {
                                serializer.NewLine(string.Format("if(null!={0}.{1})", "_this", mfmd.Name));
                                serializer.BeginBlock();
                                {
                                    // 标签+长度+类型+内容
                                    // 标签
                                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.List, ns);", nFieldIndex++));
                                    // 长度
                                    serializer.NewLine(string.Format("UInt16 count = (UInt16){0}.{1}.Count;", "_this", mfmd.Name));
                                    WritePrimitiveType(typeof(UInt16), "count", 0, serializer,false);
                                    // 类型
                                    Type genericType = MessageFieldDescription.GetGenericType(mfmd.Type);
                                    mmp.MessageFieldType mft = GetFieldType(genericType);
                                    if (mft != mmp.MessageFieldType.ObjectStart)
                                    {
                                        serializer.NewLine(string.Format("ns.WriteByte((byte)MessageFieldType.{0});", mft));
                                    }
                                    
                                    // 内容
                                    serializer.NewLine("for(UInt16 i = 0;i<count;++i)");
                                    serializer.BeginBlock();
                                    {
                                        switch (MessageFieldDescription.GetMessageFieldType(genericType))
                                        {
                                            case MessageFieldType.PrimitiveType:
                                                {
                                                    WritePrimitiveType(genericType,string.Format("{0}.{1}[i]", "_this", mfmd.Name),0,serializer,false);
                                                    break;
                                                }
                                            case MessageFieldType.ClientType:
                                                {
                                                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.ObjectStart, ns);", 0));
                                                    serializer.NewLine(string.Format("{0}.{1}[i].Write(ns);", "_this", mfmd.Name));
                                                    break;
                                                }
                                            case MessageFieldType.GenericType:
                                                {
                                                    serializer.NewLine("模板中嵌套模板不支持");
                                                    break;
                                                }
                                        }
                                    }
                                    serializer.EndBlock();
                                }
                                serializer.EndBlock();
                                break;
                            }
                        case MessageFieldType.ClientType:
                            {
                                serializer.NewLine(string.Format("if(null!={0}.{1})", "_this", mfmd.Name));
                                serializer.BeginBlock();
                                {
                                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.ObjectStart, ns);", nFieldIndex++));
                                    serializer.NewLine(string.Format("{0}.{1}.Write(ns);", "_this", mfmd.Name));
                                }                                
                                serializer.EndBlock();
                                break;
                            }
                    }
                }
                serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.ObjectEnd, ns);", 0));
            }
            serializer.EndBlock();
        }

        private mmp.MessageFieldType GetFieldType(Type type)
        {
            if (type == typeof(sbyte))
            {
                return mmp.MessageFieldType.Int8;
            }

            if (type == typeof(byte))
            {
                return mmp.MessageFieldType.Int8;
            }

            if (type == typeof(Int16))
            {
                return mmp.MessageFieldType.Int16;
            }

            if (type == typeof(UInt16))
            {
                return mmp.MessageFieldType.Int16;
            }

            if (type == typeof(Int32))
            {
                return mmp.MessageFieldType.Int32;
            }

            if (type == typeof(UInt32))
            {
                return mmp.MessageFieldType.Int32;
            }

            if (type == typeof(Int64))
            {
                return mmp.MessageFieldType.Int64;
            }

            if (type == typeof(UInt64))
            {
                return mmp.MessageFieldType.Int64;
            }

            if (type == typeof(float))
            {
                return mmp.MessageFieldType.Int32;
            }

            if (type == typeof(double))
            {
                return mmp.MessageFieldType.Int64;
            }

            if (type == typeof(bool))
            {
                return mmp.MessageFieldType.Int8;
            }

            if (type == typeof(DateTime))
            {
                return mmp.MessageFieldType.String;
            }

            if (type == typeof(String))
            {
                return mmp.MessageFieldType.String;
            }

            return mmp.MessageFieldType.ObjectStart;
        }

        private void WritePrimitiveType(Type type, string Name, int nFieldIndex,IMessageSerializer serializer,bool needTag)
        {
            if (type == typeof(sbyte))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int8, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteInt8({0});", Name));
                return;
            }

            if (type == typeof(byte))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int8, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteUInt8({0});", Name));
                return;
            }

            if (type == typeof(Int16))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int16, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteInt16({0});", Name));
                return;
            }

            if (type == typeof(UInt16))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int16, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteUInt16({0});", Name));
                return;
            }

            if (type == typeof(Int32))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int32, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteInt32({0});", Name));
                return;
            }

            if (type == typeof(UInt32))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int32, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteUInt32({0});", Name));
                return;
            }

            if (type == typeof(Int64))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int64, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteInt64({0});", Name));
                return;
            }

            if (type == typeof(UInt64))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int64, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteUInt64({0});", Name));
                return;
            }

            if (type == typeof(float))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int32, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteFloat({0});", Name));
                return;
            }

            if (type == typeof(double))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int64, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteFloat((float){0});", Name));
                return;
            }

            if (type == typeof(bool))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int8, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteBool({0});", Name));
                return;
            }

            if (type.IsEnum)
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.Int32, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteInt32((Int32){0});", Name));
                return;
            }

            if (type == typeof(DateTime))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.String, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteString({0}.ToString());", Name));
                return;
            }

            if (type == typeof(String))
            {
                if (needTag)
                {
                    serializer.NewLine(string.Format("MessageFieldTag.WriteTag({0}, MessageFieldType.String, ns);", nFieldIndex));
                }                
                serializer.NewLine(string.Format("ns.WriteString({0});", Name));
                return;
            }
        }

        private void EndWrite(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private void GenerateRead(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine(string.Format("public static void Read(this {0} {1},NetStream ns)", description.Type.Name, "_this"));
            serializer.BeginBlock();
            {
                serializer.NewLine("UInt16 nFieldIndex;");
                serializer.NewLine("MessageFieldType nFieldType;");
                serializer.NewLine("MessageFieldTag.ReadTag(out nFieldIndex, out nFieldType,ns);");
                serializer.NewLine("while(nFieldType != MessageFieldType.ObjectEnd)");
                serializer.BeginBlock();
                {
                    serializer.NewLine("switch(nFieldIndex)");
                    serializer.BeginBlock();
                    {
                        UInt16 nFieldIndex = 0;
                        foreach (MessageFieldMemberDescription mfmd in description.Members)
                        {
                            serializer.NewLine(string.Format("case {0}:", nFieldIndex));
                            serializer.BeginBlock();
                            {
                                switch (mfmd.GetMessageFieldType())
                                {
                                    case MessageFieldType.PrimitiveType:
                                        {
                                            ReadPrimitiveType(mfmd.Type, string.Format("{0}.{1}", "_this", mfmd.Name), serializer);
                                            break;
                                        }
                                    case MessageFieldType.GenericType:
                                        {
                                            // 标签+长度+类型+内容
                                            // 标签
                                            serializer.NewLine(string.Format("{0}.{1} = new {2}();", "_this", mfmd.Name, mfmd.GenericTypeName));
                                            Type genericType = MessageFieldDescription.GetGenericType(mfmd.Type);
                                            serializer.NewLine(string.Format("int length = ns.ReadUInt16();"));
                                            mmp.MessageFieldType mft = GetFieldType(genericType);
                                            if (mft != mmp.MessageFieldType.ObjectStart)
                                            {
                                                serializer.NewLine("MessageFieldType nInnerFieldType = (MessageFieldType)ns.ReadByte();");
                                            }                                            
                                            serializer.NewLine("while (length-- > 0)");
                                            serializer.BeginBlock();
                                            {
                                                switch (MessageFieldDescription.GetMessageFieldType(genericType))
                                                {
                                                    case MessageFieldType.PrimitiveType:
                                                        {
                                                            serializer.NewLine(string.Format("{0} member;", genericType.Name));
                                                            ReadPrimitiveType(genericType, "member", serializer);
                                                            serializer.NewLine(string.Format("{0}.{1}.Add(member);", "_this", mfmd.Name));
                                                            break;
                                                        }
                                                    case MessageFieldType.ClientType:
                                                        {
                                                            serializer.NewLine("MessageFieldType mft = (MessageFieldType)ns.ReadByte();");
                                                            serializer.NewLine(string.Format("{0} member = new {0}();", genericType.Name));
                                                            serializer.NewLine("member.Read(ns);");
                                                            serializer.NewLine(string.Format("{0}.{1}.Add(member);", "_this", mfmd.Name));
                                                            break;
                                                        }
                                                    case MessageFieldType.GenericType:
                                                        {
                                                            serializer.NewLine("模板嵌套模板不支持");
                                                            break;
                                                        }    
                                                }
                                            }
                                            serializer.EndBlock();
                                            break;
                                        }
                                    case MessageFieldType.ClientType:
                                        {
                                            serializer.NewLine(string.Format("{0}.{1} = new {2}();", "_this", mfmd.Name, mfmd.Type.Name));
                                            serializer.NewLine(string.Format("{0}.{1}.Read(ns);", "_this", mfmd.Name));
                                            break;
                                        }
                                }
                                serializer.NewLine("break;");
                            }
                            serializer.EndBlock();
                            ++nFieldIndex;
                        }
                        serializer.NewLine("default:");
                        serializer.BeginBlock();
                        {
                            serializer.NewLine("UnknownField.Read(nFieldType, ns);");
                            serializer.NewLine("break;");
                        }
                        serializer.EndBlock();
                    }                    
                    serializer.EndBlock();
                }
                serializer.NewLine("MessageFieldTag.ReadTag(out nFieldIndex, out nFieldType,ns);");
                serializer.EndBlock();
            }
            serializer.EndBlock();
        }

        private void ReadPrimitiveType(Type type, string Name,IMessageSerializer serializer)
        {
            if (type == typeof(sbyte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt8();", Name));
                return;
            }

            if (type == typeof(byte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt8();", Name));
                return;
            }

            if (type == typeof(Int16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt16();", Name));
                return;
            }

            if (type == typeof(UInt16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt16();", Name));
                return;
            }

            if (type == typeof(Int32))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", Name));
                return;
            }

            if (type == typeof(UInt32))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteUInt32();", Name));
                return;
            }

            if (type == typeof(Int64))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteInt64();", Name));
                return;
            }

            if (type == typeof(UInt64))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt64();", Name));
                return;
            }

            if (type == typeof(float))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", Name));
                return;
            }

            if (type == typeof(double))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", Name));
                return;
            }

            if (type == typeof(bool))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadBool();", Name));
                return;
            }

            if (type.IsEnum)
            {
                serializer.NewLine(string.Format("{0} = ({1})ns.ReadInt32();", Name, type.Name));
                return;
            }

            if (type == typeof(DateTime))
            {
                serializer.BeginBlock();
                {
                    serializer.NewLine("DateTime tmpDate;");
                    serializer.NewLine("if(DateTime.TryParse(ns.ReadString(),out tmpDate))");
                    serializer.BeginBlock();
                    {
                        serializer.NewLine(string.Format("{0} = tmpDate;", Name));
                    }                    
                    serializer.EndBlock();
                }
                serializer.EndBlock();
                return;
            }

            if (type == typeof(String))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadString();", Name));
                return;
            }
        }
    }
}
